/*
 * This file referenced Lionel Brits & Jeff Molofee's openGL Tutorials,The tutorials get from http://nehe.gamedev.net/
 */
#define GL_GLEXT_PROTOTYPES

#include <GL/glut.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <math.h>

#define IMG_W 256
#define IMG_H 256

float points[45][45][3];
int wiggle_count;

GLboolean active;

GLfloat hold;				// Temporarily Holds A Floating Point Value

GLuint texture[1];			// Storage For 1 Textures

GLubyte *loadBmp(const char *filename,unsigned int w,unsigned int h)
{
	unsigned char *buf;
	int fd = open(filename,O_RDONLY);
	if(fd < 0){
		printf("open file failed,errno:%s\n",strerror(errno));
		return NULL;
	}
	unsigned long buf_count = w * h * 3 * sizeof(unsigned char);
	buf = (unsigned char *)malloc(buf_count);
	if(!buf){
		printf("alloc memery failed,errno:%s\n",strerror(errno));
		close(fd);
		return NULL;
	}
	lseek(fd,54,SEEK_SET);
	if(read(fd,buf,buf_count) <= 0){
		printf("read file failed,errno:%s\n",strerror(errno));
		close(fd);
		free(buf);
		return NULL;
	}
	close(fd);
	return buf;
}

void init(void)
{
	GLubyte *image = loadBmp("bg.bmp",IMG_W,IMG_H);

	glGenTextures(1,&texture[0]);						// Create Three Textures
	glBindTexture(GL_TEXTURE_2D,texture[0]);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
	glTexImage2D(GL_TEXTURE_2D,0,3,IMG_W,IMG_H,0,GL_RGB,GL_UNSIGNED_BYTE,image);

	free(image);

	glEnable(GL_TEXTURE_2D);
	glShadeModel(GL_SMOOTH);
	glClearColor(0.0f,0.0f,0.0f,0.5f);
	glClearDepth(1.0f);
	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LEQUAL);
	glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);
	glPolygonMode(GL_BACK,GL_FILL);						// Back Face Is Solid
	/*depend open-GL version*/
	//glPolygonMode(GL_FRONT,GL_LINE);					// Front Face Is Made Of Lines

	int x,y;
	for(x = 0;x < 45;x++){
		for(y = 0;y < 45;y++){
			points[x][y][0] = (float)x / 5.0f - 4.5f;
			points[x][y][1] = (float)y / 5.0f - 4.5f;
			points[x][y][2] = sin(((((float)x / 5.0f) * 40.0f) / 360.0f) * 3.1415926 * 2.0f);
		}
	}
}

void display(void)
{
	int x,y;
	float f_x,f_y,f_xb,f_yb;

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glLoadIdentity();

	glTranslatef(0.0f,0.0f,-12.0f);

	glRotatef(0.3f,1.0f,0.0f,0.0f);
	glRotatef(0.2f,0.0f,1.0f,0.0f);
	glRotatef(0.4f,0.0f,0.0f,1.0f);

	glBegin(GL_QUADS);
	for(x = 0;x < 44;x++){
		for(y = 0;y < 44;y++){
			f_x = (float)x / 44.0f;
			f_y = (float)y / 44.0f;
			f_xb = (float)(x + 1) / 44.0f;
			f_yb = (float)(y + 1) / 44.0f;

			glTexCoord2f(f_x,f_y);
			glVertex3f(points[x][y][0],points[x][y][1],points[x][y][2]);

			glTexCoord2f(f_x,f_yb);
			glVertex3f(points[x][y + 1][0],points[x][y + 1][1],points[x][y + 1][2]);

			glTexCoord2f(f_xb,f_yb);
			glVertex3f(points[x +1][y + 1][0],points[x + 1][y + 1][1],points[x + 1][y + 1][2]);

			glTexCoord2f(f_xb,f_y);
			glVertex3f(points[x +1][y][0],points[x + 1][y][1],points[x + 1][y][2]);
		}
	}
	glEnd();

	glFlush();
}

void spin_display(void)
{
	int x,y;
	if(2 == wiggle_count){
		for(y = 0;y < 45;y++){
			hold = points[0][y][2];
			for(x = 0;x < 44;x++){
				points[x][y][2] = points[x + 1][y][2];
			}
			points[44][y][2] = hold;
		}
		wiggle_count = 0;
	}

	wiggle_count++;

	glutPostRedisplay();
}

void reshape(int w, int h)
{
	glViewport(0,0,w,h);								// Reset The Current Viewport

	glMatrixMode(GL_PROJECTION);						// Select The Projection Matrix
	glLoadIdentity();									// Reset The Projection Matrix

	// Calculate The Aspect Ratio Of The Window
	gluPerspective(45.0f,(GLfloat)w / (GLfloat)h,0.1f,100.0f);

	glMatrixMode(GL_MODELVIEW);							// Select The Modelview Matrix
	glLoadIdentity();									// Reset The Modelview Matrix
}

void keyboard (unsigned char key, int x, int y)
{
	switch (key){
		case 'a':
		case 'A':
			active = !active;
			glutIdleFunc(active ? spin_display : NULL);
			break;
		case 27:
			exit(0);
			break;
		default:
			return;
	}
	glutPostRedisplay();
}

int main(int argc, char** argv)
{
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);
	glutInitWindowSize(800, 600);
	glutInitWindowPosition(100, 100);
	glutCreateWindow(argv[0]);
	init();
	glutDisplayFunc(display);
	glutReshapeFunc(reshape);
	glutKeyboardFunc(keyboard);
	glutMainLoop();
	return 0;
}
